import { traverseNode } from './dom'

/** 搜索的核心方法，得出搜索的关键字在字符串中出现的索引(忽略大小写)，可用于高亮关键字(ee-highlight)
 * 
 * 具体参数参见 Array.prototype.search 扩展方法
 * 
 * @template T
 * @template U
 * @param {T[]} array
 * @param {string} keyword
 * @param {function(T): string} funcString
 * @param {function(T, search): Array<U | T | { search: search } | search | { item: T }>} onSearch
 */
export function searchCore(array, keyword, funcString, onSearch) {
  if (keyword == undefined)
    keyword = "";
  const k = keyword.toLowerCase();
  const items = [];
  let
    label,    // 当前字符串
    l,        // 当前字符串的小写
    i,        // 搜索关键词的索引
    indexes   // 搜索到的关键词在字符串中的索引数组
  for (const item of array) {
    label = (funcString?.(item) || item);
    if (!label)
      continue;
    l = label?.toLowerCase();
    i = 0;
    indexes = undefined;
    if (keyword.length) {
      while (true) {
        i = l.indexOf(k, i);
        if (i < 0)
          break;
        if (!indexes)
          indexes = [];
        indexes.push(i);
        i += k.length;
      }
    }
    const search = { keyword: keyword.length, indexes, label, getSplitIndexes };
    const ret = onSearch?.(item, search);
    if (funcString)
      item.search = search;
    if (indexes) {
      if (ret)
        items.push(ret);
      else
        if (funcString) {
          items.push(item);
        } else {
          search.item = item;
          items.push(search);
        }
    }
  }
  return items;
}

/** 根据 Array.prototype.search 方法返回的 search 对象拆分字符串的关键词和非关键词部分
 * @param {search} search 
 * @returns {keywords[]} - 将搜索到的关键字拆分为关键词和非关键词数组
 */
export function getSplitIndexes(search) {
  search = search || this;
  const indexes = search.indexes;
  const label = search.label;
  if (!indexes?.length)
    return [{ keyword: 0, label }];
  const keyword = search.keyword;
  /** @type {keywords[]} */
  const result = [];
  let from = 0;
  for (const index of indexes) {
    if (index != from)
      result.push({ keyword: 0, label: label.substring(from, index) });
    from = index + keyword;
    result.push({ keyword: 1, label: label.substring(index, from) })
  }
  if (label.length > from)
    result.push({ keyword: 0, label: label.substring(from, label.length) });
  return result;
}

/** 在 DOM 元素里搜索并高亮关键词
 * @param {HTMLElement} dom - 要搜索的 DOM 元素
 * @param {string} [keyword] - 要搜索的关键字，关键字为空可以取消高亮
 * @returns {Node[]} - 找到包含关键词的所有节点
 */
export function searchDOM(dom, keyword) {
  // 先还原已经替换过的部分，也相当于取消高亮
  const replaced = document.querySelectorAll(".ee-highlight-c");
  for (const item of replaced) {
    item.replaceWith(item.__text);
  }
  if (keyword) {
    /** @type {ChildNode[]} */
    const nodes = [];
    traverseNode(dom, (e) => {
      if (e.nodeType === Node.TEXT_NODE) {
        const data = e.nodeValue.trim();
        if (data)
          nodes.push(e);
      }
    })
    nodes.search(keyword, i => i.data, (node, search) => {
      if (search.indexes) {
        // 将文本节点替换一个 span 节点
        const r = document.createElement("span");
        r.classList.add('ee-highlight-c');
        r.__text = node;
        r.search = search.label;
        // 将文本节点包含的关键词用 span 节点高亮显示
        const splits = search.getSplitIndexes();
        const result = [];
        for (const split of splits) {
          if (split.keyword)
            result.push(`<span class="ee-highlight">${split.label}</span>`);
          else
            result.push(split.label);
        }
        r.innerHTML = result.join('');
        node.replaceWith(r);
      }
    })
  }
}